package utils

import chisel3._
import chisel3.util._
import chisel3.SyncReadMem.{ReadFirst, ReadUnderWrite, Undefined, WriteFirst}
import chisel3.util.experimental._
import chiseltest._

class SimplexPortA(val sets: Int) extends Bundle {
  val addr = Output(UInt(log2Up(sets).W))
}

class SimplexPortAW[T <: Data](private val DataType: T, sets: Int, ways: Int ) extends SimplexPortA(sets) {
  val data = Output(DataType)
  val mask = if (ways > 1) Some(Output(UInt(ways.W))) else None
}

class SimplexPortR[T <: Data](private val DataType: T, ways: Int) extends Bundle {
  val data = Output(Vec(ways, DataType))
}

class SimplexSavePort[T <: Data](DataType: T, sets: Int, ways: Int = 1) extends Bundle {
  val req = Decoupled(new SimplexPortAW(DataType, sets, ways))
}


class SimplexLoadPort[T <: Data](DataType: T, sets: Int, ways: Int = 1) extends Bundle {
  val req = Decoupled(new SimplexPortA(sets))
  val resp = Flipped(new SimplexPortR(DataType, ways))
}
class Simplex[T <: Data](DataType: T, sets: Int, ways: Int = 1) extends Module {

  val port = IO(new Bundle {
    val save = Flipped(new SimplexSavePort(DataType, sets, ways))
    val load = Flipped(new SimplexLoadPort(DataType, sets, ways))
  })
  val mem = SyncReadMem(16, Vec(ways, DataType));

  when(port.save.req.valid) {
    mem.write(
      port.save.req.bits.addr, 
      VecInit(Seq.fill(ways)(port.save.req.bits.data)), 
      port.save.req.bits.mask.getOrElse("b1".U).asBools
    );
  }

  port.save.req.ready := true.B
  port.load.req.ready := true.B

  val loadData = mem
    .read(port.load.req.bits.addr, port.load.req.valid)
    .map(_.asTypeOf(DataType))
  
  port.load.resp.data := VecInit(loadData)

}